Skip to content

v0.4.0#11

Merged
nman98 merged 12 commits into
mainfrom
dev
Nov 26, 2025
Merged

v0.4.0#11
nman98 merged 12 commits into
mainfrom
dev

Conversation

@nman98

@nman98 nman98 commented Nov 26, 2025

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Serves a favicon and enriches OpenAPI docs with per-endpoint summaries, contact and license info.
  • Changes

    • Transaction endpoints now return and display multiple messages per transaction.
    • Indexing now uses commit-based validator signing data and request-aware database operations (context/timeouts).
    • Added message counters to message records and responses.
  • Tests

    • Added test, test-race and integration-test targets.
  • Documentation

    • Added 0.4.0 release notes and expanded API external references.

✏️ Tip: You can customize this high-level summary in your review settings.

- Updated the Makefile to include  test commands for the indexer.
- Modified integration test configuration to use specific database user and name.
- Refactored `ProcessValidatorSignings` to handle commits instead of blocks, updating related methods and tests accordingly more detail in the CHANGELOG once all has been implemented.
- Implemented new methods for retrieving commits in the query operator.
- Added to synthetic query operator a function to generate and return synthetic commits for testing.
- Improved error handling and logging for commit retrieval.
- Updated tests to cover new commit functionality and ensure proper behavior.
- Added a `message_counter` field to message types (MsgSend, MsgCall, MsgAddPackage, MsgRun) to track message order and have unique message counter.
- Updated the `GetMessageFromStdTx` method to include `message_counter` in the processed messages.
- Modified error handling in the database insertion methods to log transaction hashes on failure.
- Refactored the `Orchestrator` to streamline chunk processing and improve concurrency in block and commit retrieval.
- Updated all database interaction methods to accept a context parameter, allowing for better control over timeouts and cancellation.
- Modified related methods in the address cache and data processor to utilize the new context-aware database methods.
- Data processor now processes the data using map[string]struct{} and sync mux.
- Database queries should now return the message counter for the api queries.
- Updated API handlers to pass context to database methods.
- Refactored transaction handling to support multiple message types within a single transaction, including adjustments to the response structure.
- Added favicon support to the API.
- Improved OpenAPI documentation with detailed descriptions for various endpoints.
@coderabbitai

coderabbitai Bot commented Nov 26, 2025

Copy link
Copy Markdown

Walkthrough

Threads context.Context across DB/RPC layers, adds per-message message_counter and DB/schema fields, introduces commit RPCs and commit-based processing alongside blocks, refactors orchestrator/data-processor flows for chunked parallel fetches, and updates OpenAPI, tooling, tests, and workflows.

Changes

Cohort / File(s) Summary
Workflows
\.github/workflows/golangci.yml, \.github/workflows/gotest.yml, \.github/workflows/integration-tests.yml
Add top-level permissions (contents/packages: read); expand gotest triggers to include pull_request on branches; integration-tests workflow updates flags and permissions.
Changelog & Build
CHANGELOG.md, Makefile, go.mod
Add 0.4.0 release notes; add Makefile test targets and GIT_BRANCH/VERSION logic; switch ldflags to fully-qualified module paths and build indexer/indexer.go; bump Go version to 1.25.4; remove install-experimental target.
API & OpenAPI
api/main.go, api/handlers/*.go, api/huma-types/transaction.go, api/handlers/interface.go
Propagate ctx into DB calls; transactions handler aggregates multiple message types into map[int16]; add TransactionBasicGetOutput and GetLastXTransactions; embed favicon and populate OpenAPI metadata and per-route summaries.
API tests / mocks
api/handlers/database_test.go
Update mock DB signatures to accept context and return slices where applicable; adjust internal mock maps (msgTypes → map[string][]string).
DB queries (API & indexer)
pkgs/database/queries_api.go, pkgs/database/queries_indexer.go
Thread ctx through queries; convert many single-result msg methods to multi-row returns/slices (GetBankSend, GetMsgCall, GetMsgAddPackage, GetMsgRun, GetMsgTypes); update GetAddressTxs/GetLastXTransactions and indexer-specific queries to accept context.
DB inserts & types
pkgs/database/insert.go, pkgs/database/types_api.go, pkgs/sql_data_types/table.go
Add ctx parameter to all Insert* methods and pass ctx into pgx.CopyFrom; add MessageCounter int16 field to BankSend, MsgCall, MsgAddPackage, MsgRun (DB/API/sql types).
Address cache
indexer/address_cache/*
DatabaseForAddresses interface now accepts ctx; calls wrapped with context.WithTimeout (5–10s) and canceled; GetAllAddresses uses a 10s timeout.
Data processor
indexer/data_processor/operator.go, indexer/data_processor/types.go, indexer/data_processor/operator_test.go
Replace sync.Map with mutex-guarded maps for deduplication; add ctx/timeouts to Insert* DB calls; ProcessValidatorSignings now consumes commits; tests updated to call mocks with context.
Decoder & product mapping
indexer/decoder/decoder.go, indexer/decoder/product.go
Add per-message message_counter (int16) with overflow guard (<=32767); include message_counter in emitted maps and populate MessageCounter in DB conversion (required).
Orchestrator & QueryOperator
indexer/orchestrator/*, indexer/query/*, indexer/orchestrator/types.go
Add GetFromToCommits and GetCommit flow; orchestrator refactored to unified processChunk fetching blocks+commits in parallel; DataProcessor.ProcessValidatorSignings signature updated to accept commits; GetLastBlockHeight becomes context-aware.
RPC client – commit support
indexer/rpc_client/*
Add RequestCommit constant, GetCommit RPC method and types (CommitResponse, CommitResult, etc.), RpcCommitError, and rate-limited GetCommit/TryGetCommit implementations.
Tests / synthetic infra
integration/synthetic/*, integration/test_config.example.yml, indexer/query/operator_test.go, indexer/orchestrator/operator_test.go
Add ResponseMaker and synthetic commits generation; SyntheticQueryOperator stores commits and exposes GetFromToCommits; mocks/tests updated for commits and context-aware GetLastBlockHeight; example max_height/to_height reduced.
Misc / docs / experiments
indexer/*, pkgs/database/pool.go, pkgs/generator/generator_test.go, experiments/*
Replace "Args:" → "Parameters:" in comments; convert one experiment test to main executable; expand compression experiment; replace prints with testify assertions in generator tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client as HTTP Client
    participant Handler as API Handler
    participant DB as TimescaleDb
    participant Ctx as Context

    Client->>Handler: HTTP request
    Handler->>Ctx: create ctx (with timeout)
    Handler->>DB: GetMsgTypes(ctx, txHash, chain)
    DB-->>Handler: []string (message types)
    loop per message type
        Handler->>DB: Get<MsgX>(ctx, txHash, chain) -> []*MsgX (each has MessageCounter)
        DB-->>Handler: messages
        Handler->>Handler: place messages into map[int16]Response
    end
    Handler-->>Client: multi-message response (map[int16]TransactionMessage)
Loading
sequenceDiagram
    participant Orch as Orchestrator
    participant QO as QueryOperator
    participant RPC as RpcClient
    participant DP as DataProcessor

    Orch->>QO: GetFromToBlocks(from,to)
    Orch->>QO: GetFromToCommits(from,to) [NEW]
    par parallel fetch
        QO->>RPC: GetBlock(height) x N
        QO->>RPC: GetCommit(height) x N [NEW]
    end
    QO-->>Orch: blocks, commits
    Orch->>DP: ProcessValidatorSignings(commits, from,to) [CHANGED]
    DP-->>Orch: processed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas to focus review on:

  • indexer/data_processor/operator.go — concurrency refactor (sync.Map → mutex), dynamic timeouts, signature changes.
  • indexer/orchestrator/operator.go — chunking refactor, parallel commits+blocks fetch, updated progress metrics.
  • pkgs/database/queries_api.go & insert.go — ctx propagation, multi-row scans, pgx.CopyFrom(ctx,...), return-shape changes.
  • api/handlers/transactions.go — multi-message aggregation correctness, message counter mapping, error handling.
  • decoder ↔ product ↔ DB schema — message_counter propagation and int16 bounds across decoder, product conversion, and DB types.

Possibly related PRs

  • v0.3.0 #9 — touches similar handler and database interfaces and earlier transaction/block retrieval changes that this PR extends (ctx, commits, multi-message support).

Poem

🐇 I hopped through code with context in paw,
Counting messages — each one I saw,
Commits and blocks now dance in a pair,
Timeouts set gentle, inserted with care,
A tiny rabbit cheers — functions all fair.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'v0.4.0' is a version tag rather than a descriptive summary of the changeset's primary purpose or modifications. Replace the title with a concise description of the main changes, such as 'Add context propagation and message counter support' or 'Refactor to support commit-based validator signings'.
Docstring Coverage ⚠️ Warning Docstring coverage is 61.11% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
indexer/db_init/hypertable.go (1)

90-90: Fix typo in AddCompressionPolicy documentation.

Line 90 has "TThis" which should be "This".

-// TThis function specifies the compression policy
+// This function specifies the compression policy
.github/workflows/integration-tests.yml (1)

60-60: I'll help you verify the consistency of the database setup commands across the three integration test jobs. Let me start by examining the workflow file to understand the full context.
<function_calls>

cat -n .github/workflows/integration-tests.yml | head -250

</function_calls>

sed -n '50,70p' .github/workflows/integration-tests.yml

</function_calls>

sed -n '125,145p' .github/workflows/integration-tests.yml

</function_calls>

sed -n '200,215p' .github/workflows/integration-tests.yml

</function_calls>

Now let me check the indexer command to understand what the flags do and whether they have defaults:
<function_calls>

find . -name "main.go" -path "/indexer/" | head -5

</function_calls>
<function_calls>

rg -n "create-db" --type=go -A 10 -B 5

</function_calls>

Let me get more information about the command definition:
<function_calls>

rg -n "db-user|db-name" --type=go -B 5 -A 2 | head -100

</function_calls>

Let me search more broadly for the setup command definition:
<function_calls>

rg -n "setup" --type=go | grep -i "command|flag" | head -20

</function_calls>

Let me look at the environment variables defined in the workflow:
<function_calls>

sed -n '1,100p' .github/workflows/integration-tests.yml | grep -A 20 "env:"

</function_calls>

<function_calls>

sed -n '50,100p' .github/workflows/integration-tests.yml

</function_calls>

Let me get a comprehensive view of the entire workflow file structure:
<function_calls>

wc -l .github/workflows/integration-tests.yml

</function_calls>

<function_calls>

cat .github/workflows/integration-tests.yml

</function_calls>

Perfect! Now let me search for the command implementation to understand the flag behavior:
<function_calls>

find . -type f -name "*.go" | xargs grep -l "create-db" | head -5

</function_calls>

<function_calls>

rg -n "create-db|createDB" --type=go -B 3 -A 15 | head -150

</function_calls>

Let me search more specifically:
<function_calls>

rg -n "flag.(String|Bool|Int)" --type=go | grep -E "(db.user|db.name|db_user|db_name)" | head -20

</function_calls>

<function_calls>

fd "main.go" --path "indexer" -x cat {}

</function_calls>

Let me try a different approach to find where flags are defined:
<function_calls>

rg -n "db-user|db-name" --type=go | head -30

</function_calls>

<function_calls>

rg -n "dbUser|dbName" --type=go -B 2 -A 2 | head -50

</function_calls>

Let me check the repository structure:
<function_calls>

git ls-files | grep -E "indexer|cmd" | head -20

</function_calls>

<function_calls>

git ls-files "indexer/" "cmd/" | head -30

</function_calls>

<function_calls>

ls -la indexer/ 2>/dev/null || ls -la cmd/ 2>/dev/null || find . -maxdepth 2 -type d -name "indexer" -o -name "cmd" 2>/dev/null

</function_calls>

Let me check what files are actually in the repo:
<function_calls>

git ls-files | head -50

</function_calls>

Now let me search for any references to the setup command:
<function_calls>

rg -n "setup" --type=go -B 2 -A 8 | head -200

</function_calls>

<function_calls>

rg -n "postgres" --type=go | head -40

</function_calls>

Let me check the environment variables more carefully in all three jobs:
<function_calls>

grep -n "db-user|db-name|DB_USER|DB_NAME" .github/workflows/integration-tests.yml

</function_calls>

<function_calls>

python3 << 'EOF'
import re

with open('.github/workflows/integration-tests.yml', 'r') as f:
content = f.read()

Find all test job sections

jobs = re.findall(r'integration-tests-(small|medium|large):(.*?)(?=\n \w+-tests-\w+:|$)', content, re.DOTALL)

for job_name, job_content in jobs:
# Find the create-db command
cmd_match = re.search(r'go run indexer/main.go setup create-db[^\n]*', job_content)
if cmd_match:
print(f"\n=== {job_name.upper()} ===")
print(f"Command: {cmd_match.group(0)}")

# Find env vars
env_match = re.search(r'env:(.*?)(?=\n\s{6}\w+:|$)', job_content, re.DOTALL)
if env_match:
    print(f"Environment variables:")
    for line in env_match.group(1).strip().split('\n'):
        if line.strip():
            print(f"  {line.strip()}")

EOF


</function_calls>

Perfect! Now I have clear visibility. Let me verify if there's any documentation about default values for these flags:
<function_calls>

rg -n "DB_USER|DB_NAME" --type=go -B 5 -A 5 | head -100

</function_calls>

<function_calls>

rg -n "getenv|os." --type=go | grep -i "db_user|db_name" | head -20

</function_calls>

Let me search for any flag defaults or initial values:
<function_calls>

rg -n "NewCommand|NewRunner" --type=go -A 20 | head -100

</function_calls>

Let me check git history to see if this was intentional or a mistake:
<function_calls>

git log --oneline -20 -- .github/workflows/integration-tests.yml

</function_calls>

<function_calls>

git log -p --max-count=5 -- .github/workflows/integration-tests.yml | head -300

</function_calls>

Let me check if there are any recent changes to the large test job specifically:
<function_calls>

git show HEAD:.github/workflows/integration-tests.yml | sed -n '200,215p'

</function_calls>

Based on my analysis of the workflow file, I can confirm the inconsistency exists as stated in the original review comment:

  • Line 60 (small test): go run indexer/main.go setup create-db --db-user postgres --db-name postgres
  • Line 134 (medium test): go run indexer/main.go setup create-db --db-user postgres --db-name postgres
  • Line 208 (large test): go run indexer/main.go setup create-db

All three jobs define DB_USER: postgres in their environment variables, but the small and medium tests additionally pass the --db-user postgres --db-name postgres flags explicitly, while the large test omits them.

While I cannot access the Go implementation details in this sandbox environment to determine whether the flags are truly redundant or necessary, the clear inconsistency across test sizes without any code comments explaining the difference suggests this is unintentional. The pattern of flags being included in two places but omitted in the third is characteristic of a configuration inconsistency that would benefit from clarification or correction.


Database setup command flags should be consistent across all integration test sizes.

The small and medium test jobs include --db-user postgres --db-name postgres flags, while the large test job omits them despite setting the same DB_USER: postgres environment variable. This inconsistency could indicate either:

  1. An unintentional omission in the large test that should be corrected to match the others
  2. A deliberate difference that should be documented

Verify whether the large test should include the same flags:

      - name: Initialize database schema
        run: |
-         go run indexer/main.go setup create-db
+         go run indexer/main.go setup create-db --db-user postgres --db-name postgres
        env:

If the flags are truly redundant with environment variables, remove them consistently from all three test jobs for clarity and maintainability.

experiments/experiment1/enc_dec_test.go (1)

42-43: Add newline characters to Printf statements.

Both Printf statements are missing newline characters (\n), causing the output to be printed on the same line, which makes it difficult to read.

Apply this diff to fix the formatting:

-	fmt.Printf("The final base64 decoded has %v bytes", len(rawByte))
-	fmt.Printf("The first decoded base64 has %v bytes", len(base64Decoded))
+	fmt.Printf("The final base64 decoded has %v bytes\n", len(rawByte))
+	fmt.Printf("The first decoded base64 has %v bytes\n", len(base64Decoded))
pkgs/generator/generator_test.go (1)

41-50: Fix panic recovery that hides test failures.

The panic recovery logs the error but doesn't fail the test with t.Fatal() or t.Error(). This means panics will be hidden and the test will incorrectly pass.

 // RunAllTests runs all the test functions
 func RunAllTests(t *testing.T) {
 	defer func() {
 		if r := recover(); r != nil {
-			log.Printf("Test failed with panic: %v", r)
+			t.Fatalf("Test failed with panic: %v", r)
 		}
 	}()
 
 	TestAuthenticGeneration(t)
 }
indexer/decoder/decoder.go (1)

214-223: Bug: extractCoins produces duplicate entries.

The function pre-allocates a slice with make([]Coin, len(amount)) (filled with zero values), then uses append which adds elements after the zero values, resulting in duplicate/empty entries.

Apply this diff to fix:

 func extractCoins(amount std.Coins) ([]Coin, error) {
-	// make a string and split it by space
-	coins := make([]Coin, len(amount))
-	for _, coin := range amount {
-		coins = append(coins, Coin{
+	coins := make([]Coin, 0, len(amount))
+	for _, coin := range amount {
+		coins = append(coins, Coin{
 			Amount: coin.Amount,
 			Denom:  coin.Denom,
 		})
 	}
 	return coins, nil
 }

Alternatively, use index-based assignment:

 func extractCoins(amount std.Coins) ([]Coin, error) {
-	// make a string and split it by space
 	coins := make([]Coin, len(amount))
-	for _, coin := range amount {
-		coins = append(coins, Coin{
+	for i, coin := range amount {
+		coins[i] = Coin{
 			Amount: coin.Amount,
 			Denom:  coin.Denom,
-		})
+		}
 	}
 	return coins, nil
 }
pkgs/database/queries_api.go (2)

146-156: Missing rows.Err() check in GetFromToBlocks.

After iterating through rows, you should check for any errors that occurred during iteration. This is done correctly in GetLastXBlocks (line 108) but missing here.

 	for rows.Next() {
 		block := &BlockData{}
 		err := rows.Scan(&block.Hash, &block.Height, &block.Timestamp, &block.ChainID, &block.Txs)
 		if err != nil {
 			return nil, err
 		}
 		blocks = append(blocks, block)
 	}
+	if err := rows.Err(); err != nil {
+		return nil, err
+	}
 	return blocks, nil

212-260: GetBankSend is missing message_counter in the query.

The query selects columns for BankSend but omits bms.message_counter, while other similar methods (GetMsgCall, GetMsgAddPackage, GetMsgRun) include it. The BankSend struct has a MessageCounter field (per types_api.go), so this field will always be zero in API responses.

 	query := `
 	SELECT 
+   bms.message_counter,
     encode(bms.tx_hash, 'base64') AS tx_hash,
     bms.timestamp,
     gn_from.address AS from_address,
     gn_to.address AS to_address,
     bms.amount,
     array(
         SELECT gn.address 
         FROM unnest(bms.signers) AS signer_id
         JOIN gno_addresses gn ON gn.id = signer_id
     ) AS signers
 	FROM bank_msg_send bms

And update the Scan call to include &bankSend.MessageCounter as the first field:

 		err := rows.Scan(
+			&bankSend.MessageCounter,
 			&bankSend.TxHash,
 			&bankSend.Timestamp,
🧹 Nitpick comments (19)
Makefile (1)

1-1: Consider adding the "all" phony target.

Best practice for Makefiles is to include an all target as the default goal. While your current targets work, adding all as the first PHONY target and defining it as a prerequisite to build, build-api, and build-experimental would improve discoverability and conventionality.

-.PHONY: build install clean build-experimental install-experimental build-api test-race integration-test test
+.PHONY: all build install clean build-experimental install-experimental build-api test-race integration-test test
+
+all: build build-api
CHANGELOG.md (2)

10-10: Consider more formal language for release notes.

The opening line "Mostly it has some bug fixes" uses informal phrasing. Consider revising to something like: "This release includes several bug fixes and improvements."


10-10: Consider more formal language for release notes opening.

The phrasing "Mostly it has some bug fixes" is informal for release notes. Consider revising to: "This release includes several bug fixes and improvements."

experiments/experiment1/enc_dec_test.go (1)

18-18: Consider using idiomatic Go naming convention.

The variable raw_data uses snake_case instead of the idiomatic Go camelCase convention. Consider renaming it to rawData for consistency with Go style guidelines.

Apply this diff to improve naming:

-	raw_data := "CpABCgovdm0ubV9jYWxsEoEBCihnMTcyOTBjd3ZtcmFwdnA4Njl4Zm5oaGF3YThzbTllZHB1ZnphdDdkIhZnbm8ubGFuZC9yL2dub3N3YXAvZ25zKghUcmFuc2ZlcjIoZzEyejc3bHk0aHdyeWtqMnphZmNoZmR2bGxmZm5wbDU1d2dzcHVjbDIJMTAwMDAwMDAwCpwBCgovdm0ubV9jYWxsEo0BCihnMTcyOTBjd3ZtcmFwdnA4Njl4Zm5oaGF3YThzbTllZHB1ZnphdDdkIiJnbm8ubGFuZC9yL2dub3N3YXAvdGVzdF90b2tlbi91c2RjKghUcmFuc2ZlcjIoZzEyejc3bHk0aHdyeWtqMnphZmNoZmR2bGxmZm5wbDU1d2dzcHVjbDIJMTAwMDAwMDAwCpsBCgovdm0ubV9jYWxsEowBCihnMTcyOTBjd3ZtcmFwdnA4Njl4Zm5oaGF3YThzbTllZHB1ZnphdDdkIiFnbm8ubGFuZC9yL2dub3N3YXAvdGVzdF90b2tlbi9iYXIqCFRyYW5zZmVyMihnMTJ6NzdseTRod3J5a2oyemFmY2hmZHZsbGZmbnBsNTV3Z3NwdWNsMgkxMDAwMDAwMDAKmwEKCi92bS5tX2NhbGwSjAEKKGcxNzI5MGN3dm1yYXB2cDg2OXhmbmhoYXdhOHNtOWVkcHVmemF0N2QiIWduby5sYW5kL3IvZ25vc3dhcC90ZXN0X3Rva2VuL2JheioIVHJhbnNmZXIyKGcxMno3N2x5NGh3cnlrajJ6YWZjaGZkdmxsZmZucGw1NXdnc3B1Y2wyCTEwMDAwMDAwMAqbAQoKL3ZtLm1fY2FsbBKMAQooZzE3MjkwY3d2bXJhcHZwODY5eGZuaGhhd2E4c205ZWRwdWZ6YXQ3ZCIhZ25vLmxhbmQvci9nbm9zd2FwL3Rlc3RfdG9rZW4vb2JsKghUcmFuc2ZlcjIoZzEyejc3bHk0aHdyeWtqMnphZmNoZmR2bGxmZm5wbDU1d2dzcHVjbDIJMTAwMDAwMDAwCpsBCgovdm0ubV9jYWxsEowBCihnMTcyOTBjd3ZtcmFwdnA4Njl4Zm5oaGF3YThzbTllZHB1ZnphdDdkIiFnbm8ubGFuZC9yL2dub3N3YXAvdGVzdF90b2tlbi9mb28qCFRyYW5zZmVyMihnMTJ6NzdseTRod3J5a2oyemFmY2hmZHZsbGZmbnBsNTV3Z3NwdWNsMgkxMDAwMDAwMDAKmwEKCi92bS5tX2NhbGwSjAEKKGcxNzI5MGN3dm1yYXB2cDg2OXhmbmhoYXdhOHNtOWVkcHVmemF0N2QiIWduby5sYW5kL3IvZ25vc3dhcC90ZXN0X3Rva2VuL3F1eCoIVHJhbnNmZXIyKGcxMno3N2x5NGh3cnlrajJ6YWZjaGZkdmxsZmZucGw1NXdnc3B1Y2wyCTEwMDAwMDAwMBITCICEr18SDDEwMDAwMDB1Z25vdBp+CjoKEy90bS5QdWJLZXlTZWNwMjU2azESIwohAtGyA6l3UIrUup5z7yXo90bXDcXUOmLiK34YPffgQ6pAEkCg8pAtBv6Fhw98bKYdqrEX2UrcjYvIUpbGdxMyc5Zpfl1cMNo8G6vzpnFaQESX+7eZIFTfO5BCFjVJLfC5Ur5C"
+	rawData := "CpABCgovdm0ubV9jYWxsEoEBCihnMTcyOTBjd3ZtcmFwdnA4Njl4Zm5oaGF3YThzbTllZHB1ZnphdDdkIhZnbm8ubGFuZC9yL2dub3N3YXAvZ25zKghUcmFuc2ZlcjIoZzEyejc3bHk0aHdyeWtqMnphZmNoZmR2bGxmZm5wbDU1d2dzcHVjbDIJMTAwMDAwMDAwCpwBCgovdm0ubV9jYWxsEo0BCihnMTcyOTBjd3ZtcmFwdnA4Njl4Zm5oaGF3YThzbTllZHB1ZnphdDdkIiJnbm8ubGFuZC9yL2dub3N3YXAvdGVzdF90b2tlbi91c2RjKghUcmFuc2ZlcjIoZzEyejc3bHk0aHdyeWtqMnphZmNoZmR2bGxmZm5wbDU1d2dzcHVjbDIJMTAwMDAwMDAwCpsBCgovdm0ubV9jYWxsEowBCihnMTcyOTBjd3ZtcmFwdnA4Njl4Zm5oaGF3YThzbTllZHB1ZnphdDdkIiFnbm8ubGFuZC9yL2dub3N3YXAvdGVzdF90b2tlbi9iYXIqCFRyYW5zZmVyMihnMTJ6NzdseTRod3J5a2oyemFmY2hmZHZsbGZmbnBsNTV3Z3NwdWNsMgkxMDAwMDAwMDAKmwEKCi92bS5tX2NhbGwSjAEKKGcxNzI5MGN3dm1yYXB2cDg2OXhmbmhoYXdhOHNtOWVkcHVmemF0N2QiIWduby5sYW5kL3IvZ25vc3dhcC90ZXN0X3Rva2VuL2JheioIVHJhbnNmZXIyKGcxMno3N2x5NGh3cnlrajJ6YWZjaGZkdmxsZmZucGw1NXdnc3B1Y2wyCTEwMDAwMDAwMAqbAQoKL3ZtLm1fY2FsbBKMAQooZzE3MjkwY3d2bXJhcHZwODY5eGZuaGhhd2E4c205ZWRwdWZ6YXQ3ZCIhZ25vLmxhbmQvci9nbm9zd2FwL3Rlc3RfdG9rZW4vb2JsKghUcmFuc2ZlcjIoZzEyejc3bHk0aHdyeWtqMnphZmNoZmR2bGxmZm5wbDU1d2dzcHVjbDIJMTAwMDAwMDAwCpsBCgovdm0ubV9jYWxsEowBCihnMTcyOTBjd3ZtcmFwdnA4Njl4Zm5oaGF3YThzbTllZHB1ZnphdDdkIiFnbm8ubGFuZC9yL2dub3N3YXAvdGVzdF90b2tlbi9mb28qCFRyYW5zZmVyMihnMTJ6NzdseTRod3J5a2oyemFmY2hmZHZsbGZmbnBsNTV3Z3NwdWNsMgkxMDAwMDAwMDAKmwEKCi92bS5tX2NhbGwSjAEKKGcxNzI5MGN3dm1yYXB2cDg2OXhmbmhoYXdhOHNtOWVkcHVmemF0N2QiIWduby5sYW5kL3IvZ25vc3dhcC90ZXN0X3Rva2VuL3F1eCoIVHJhbnNmZXIyKGcxMno3N2x5NGh3cnlrajJ6YWZjaGZkdmxsZmZucGw1NXdnc3B1Y2wyCTEwMDAwMDAwMBITCICEr18SDDEwMDAwMDB1Z25vdBp+CjoKEy90bS5QdWJLZXlTZWNwMjU2azESIwohAtGyA6l3UIrUup5z7yXo90bXDcXUOmLiK34YPffgQ6pAEkCg8pAtBv6Fhw98bKYdqrEX2UrcjYvIUpbGdxMyc5Zpfl1cMNo8G6vzpnFaQESX+7eZIFTfO5BCFjVJLfC5Ur5C"

And update the reference on line 21:

-	base64Decoded, err := base64.StdEncoding.DecodeString(raw_data)
+	base64Decoded, err := base64.StdEncoding.DecodeString(rawData)
experiments/experiment4/encode_proto.go (1)

4-4: Per‑level compression loop looks correct; consider minor ergonomic cleanups

The per‑level benchmarking loop is sane: fresh bytes.Buffer per iteration, encoder.Reset(&buf), write, close, and then inspect buf.Bytes() is all correct. The new bytes import is appropriate.

If you want to tighten it up a bit:

  • You can construct the writer directly with the buffer instead of NewWriter(nil) + Reset(&buf), e.g. zstandard.NewWriter(&buf, zstandard.WithEncoderLevel(encoderLevel)), which avoids one extra call.
  • The “encode into buffer and return compressed bytes” pattern is duplicated with the final level‑22 block; it could be extracted into a small helper like compressAtLevel(encoded []byte, level int) ([]byte, error) to reduce repetition, but that’s optional given this lives under experiments/.

Also applies to: 86-107

indexer/context_hook/hook.go (1)

35-41: Minor documentation inconsistency.

The Parameters section lists sh: the signal handler, but sh is a method receiver, not a parameter. Consider removing this entry or clarifying it as "Receiver" for accuracy.

 // Context returns the context that will be cancelled on shutdown signals
 //
-// Parameters:
-//   - sh: the signal handler
-//
 // Returns:
 //   - context.Context: the context
 //
indexer/query/types.go (1)

24-36: Update interface doc comment to include GetCommit.

The doc comment lists GetBlock, GetLatestBlockHeight, and GetTx but omits the newly added GetCommit method.

 // Rate limiter Gnoland RPC client interface
 //
 // # The interface is used to get the blocks, latest block height, and txs
 //
 // Methods:
 // - GetBlock: to get a block from the rpc client
 // - GetLatestBlockHeight: to get the latest block height from the rpc client
 // - GetTx: to get a tx from the rpc client
+// - GetCommit: to get a commit from the rpc client
 type RpcClient interface {
indexer/rpc_client/errors.go (1)

35-48: Consider consolidating duplicate error types.

RpcCommitError is structurally identical to RpcHeightError (same fields, same Error() implementation). If semantic type safety isn't strictly required, you could use a single generic or aliased type to reduce duplication.

However, if distinct types are intentional for call-site clarity (e.g., differentiating commit errors from block errors in type switches), the current approach is acceptable.

Example consolidation using a type alias:

// If semantic distinction isn't critical at call sites:
type RpcCommitError = RpcHeightError
indexer/decoder/product.go (1)

263-270: Optional: Consider consistent field ordering in struct literals.

MessageCounter is placed last in MsgSend but second (after TxHash) in other message types. For consistency and easier comparison, consider aligning the field order across all struct literals.

 return &dataTypes.MsgSend{
+	MessageCounter: messageCounter,
 	TxHash:         txHash,
 	ChainName:      chainName,
 	FromAddress:    addressResolver.GetAddress(fromAddress),
 	ToAddress:      addressResolver.GetAddress(toAddress),
 	Amount:         amount,
 	Signers:        signerIds,
 	Timestamp:      timestamp,
-	MessageCounter: messageCounter,
 }, nil
indexer/cmd/setup.go (1)

100-102: Context timeout implementation is correct.

The 10-second timeout context with proper defer cancel() is well-implemented. However, note that only CheckCurrentDatabaseName receives the context while subsequent database operations (CreateDatabase, SwitchDatabase, CreateSpecialTypeFromStruct, etc.) do not. Consider propagating context to these operations as well for consistent cancellation behavior throughout the setup flow.

indexer/query/operator.go (1)

143-201: GetFromToCommits correctly mirrors GetFromToBlocks pattern.

The implementation follows the established concurrent fetch pattern with proper:

  • Slice preallocation with exact size
  • Mutex-protected slice access
  • WaitGroup coordination
  • Retry mechanism with success/failure callbacks

Consider extracting the common parallel-fetch-with-retry pattern into a generic helper to reduce duplication between GetFromToBlocks, GetFromToCommits, and GetTransactions. For example:

func parallelFetch[T any](
    count int,
    fetch func(idx int) (T, error),
    retryFetch func(idx int) (T, error),
    retryConfig RetryConfig,
) []T {
    // shared implementation
}
api/handlers/transactions.go (1)

43-146: Consider renaming the loop variable to avoid shadowing.

The variable data is used both for the slice returned by the database query and as the loop iteration variable (lines 67, 84, 104, 124). While this is valid Go, it reduces clarity.

Consider renaming the loop variable for improved readability:

 data, err := h.db.GetBankSend(ctx, txHashBase64, h.chainName)
 if err != nil {
     return nil, huma.Error404NotFound(fmt.Sprintf("Failed to fetch %s data for transaction %s", msgType, input.TxHash), err)
 }
-for _, data := range data {
-    index := data.MessageCounter
+for _, msg := range data {
+    index := msg.MessageCounter
     response[index] = humatypes.TransactionMessage{
         MessageType: msgType,
-        TxHash:      data.TxHash,
-        Timestamp:   data.Timestamp,
-        Signers:     data.Signers,
-        FromAddress: data.FromAddress,
-        ToAddress:   data.ToAddress,
-        Amount:      data.Amount,
+        TxHash:      msg.TxHash,
+        Timestamp:   msg.Timestamp,
+        Signers:     msg.Signers,
+        FromAddress: msg.FromAddress,
+        ToAddress:   msg.ToAddress,
+        Amount:      msg.Amount,
     }
 }

Apply similar changes to the other message type cases (vm_msg_call, vm_msg_add_package, vm_msg_run).

integration/synthetic/query_operator.go (2)

100-113: Inconsistent nil check compared to GetFromToBlocks.

GetFromToBlocks (lines 68-82) checks if each block is non-nil before appending, but GetFromToCommits unconditionally appends the result of getCommit. Since getCommit calls log.Fatal if not found, this is safe in practice, but the pattern differs. Consider unifying the approach or documenting the invariant.

 	for height := fromHeight; height <= toHeight; height++ {
-		commits = append(commits, sq.getCommit(height))
+		commit := sq.getCommit(height)
+		if commit != nil {
+			commits = append(commits, commit)
+		}
 	}

240-252: Proposer selection is deterministic, not random as comment suggests.

Line 246 uses modulo arithmetic height%uint64(len(sq.signedValidators)) which is deterministic based on height, not random. The comment says "random validator" which is misleading.

-		ProposerAddress:  sq.signedValidators[height%uint64(len(sq.signedValidators))], // random validator
+		ProposerAddress:  sq.signedValidators[height%uint64(len(sq.signedValidators))], // deterministic proposer based on height
indexer/address_cache/address.go (1)

115-120: Consider using defer cancel() to prevent context leak on early return.

If FindExistingAccounts returns an error at line 119, the function returns immediately. The cancel() at line 117 is called, but using defer cancel() is the idiomatic pattern and protects against future code changes that might add more exit paths.

 	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+	defer cancel()
 	existingAddresses, err := a.db.FindExistingAccounts(ctx, address, chainName, insertValidators)
-	cancel()
 	if err != nil {
 		return
 	}
indexer/data_processor/operator.go (1)

62-98: Mutex-protected map is correct but consider reducing lock contention.

The pattern of locking for each individual write (lines 78-80, 86-88) works correctly but may cause contention with many concurrent goroutines. For high-volume scenarios, consider using a sync.Map or collecting addresses locally per goroutine and merging after wg.Wait().

integration/synthetic/respone_maker.go (1)

1-1: Filename typo: "respone_maker.go" should be "response_maker.go".

The filename has a typo - missing the 's' in "response".

Consider renaming the file from respone_maker.go to response_maker.go for correctness.

api/handlers/interface.go (1)

13-13: Consider using []database.AddressTx instead of *[]database.AddressTx for consistency.

The return type *[]database.AddressTx is inconsistent with other methods in this interface that return slices directly (e.g., []*database.BlockData, []*database.Transaction, []*database.BankSend). In Go, slices are already reference types, so returning a pointer to a slice is rarely necessary and can lead to nil-pointer confusion.

-	GetAddressTxs(ctx context.Context, address string, chainName string, fromTimestamp time.Time, toTimestamp time.Time) (*[]database.AddressTx, error)
+	GetAddressTxs(ctx context.Context, address string, chainName string, fromTimestamp time.Time, toTimestamp time.Time) ([]database.AddressTx, error)
pkgs/database/queries_api.go (1)

608-645: GetAddressTxs returns *[]AddressTx - consider returning []AddressTx instead.

Consistent with the earlier observation on the interface, returning a pointer to a slice is unusual in Go. The method implementation already creates the slice as a value (addressTxs := make([]AddressTx, 0)), then returns a pointer to it. This works but is inconsistent with other query methods that return []*Type.

 func (t *TimescaleDb) GetAddressTxs(
 	ctx context.Context,
 	address string,
 	chainName string,
 	fromTimestamp time.Time,
 	toTimestamp time.Time,
-) (*[]AddressTx, error) {
+) ([]AddressTx, error) {
 ...
-	return &addressTxs, nil
+	return addressTxs, nil
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ea6cbc and aa49536.

⛔ Files ignored due to path filters (1)
  • api/public/favicon.ico is excluded by !**/*.ico
📒 Files selected for processing (52)
  • .github/workflows/golangci.yml (1 hunks)
  • .github/workflows/gotest.yml (1 hunks)
  • .github/workflows/integration-tests.yml (2 hunks)
  • CHANGELOG.md (1 hunks)
  • Makefile (2 hunks)
  • api/handlers/address.go (1 hunks)
  • api/handlers/blocks.go (5 hunks)
  • api/handlers/database_test.go (10 hunks)
  • api/handlers/interface.go (2 hunks)
  • api/handlers/transactions.go (3 hunks)
  • api/huma-types/transaction.go (2 hunks)
  • api/main.go (4 hunks)
  • experiments/experiment1/enc_dec_test.go (1 hunks)
  • experiments/experiment4/encode_proto.go (2 hunks)
  • go.mod (1 hunks)
  • indexer/address_cache/address.go (9 hunks)
  • indexer/address_cache/types.go (1 hunks)
  • indexer/cmd/setup.go (2 hunks)
  • indexer/context_hook/hook.go (2 hunks)
  • indexer/data_processor/event.go (1 hunks)
  • indexer/data_processor/operator.go (24 hunks)
  • indexer/data_processor/operator_test.go (3 hunks)
  • indexer/data_processor/types.go (2 hunks)
  • indexer/db_init/hypertable.go (3 hunks)
  • indexer/db_init/tables.go (10 hunks)
  • indexer/decoder/decoder.go (8 hunks)
  • indexer/decoder/product.go (9 hunks)
  • indexer/main_operator/operator.go (2 hunks)
  • indexer/orchestrator/operator.go (12 hunks)
  • indexer/orchestrator/operator_test.go (8 hunks)
  • indexer/orchestrator/types.go (2 hunks)
  • indexer/query/operator.go (2 hunks)
  • indexer/query/operator_test.go (1 hunks)
  • indexer/query/types.go (1 hunks)
  • indexer/retry/worker.go (1 hunks)
  • indexer/rpc_client/client.go (7 hunks)
  • indexer/rpc_client/commit.go (1 hunks)
  • indexer/rpc_client/errors.go (1 hunks)
  • indexer/rpc_client/rate_limit/rate_limit.go (1 hunks)
  • indexer/rpc_client/rate_limited_client.go (3 hunks)
  • indexer/rpc_client/types.go (1 hunks)
  • integration/synthetic/init.go (2 hunks)
  • integration/synthetic/query_operator.go (6 hunks)
  • integration/synthetic/respone_maker.go (5 hunks)
  • integration/test_config.example.yml (1 hunks)
  • pkgs/database/insert.go (14 hunks)
  • pkgs/database/pool.go (5 hunks)
  • pkgs/database/queries_api.go (26 hunks)
  • pkgs/database/queries_indexer.go (7 hunks)
  • pkgs/database/types_api.go (1 hunks)
  • pkgs/generator/generator_test.go (1 hunks)
  • pkgs/sql_data_types/table.go (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (24)
indexer/rpc_client/types.go (2)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
indexer/rpc_client/errors.go (1)
  • RpcCommitError (36-40)
pkgs/generator/generator_test.go (3)
pkgs/generator/general.go (5)
  • NewDataGenerator (43-54)
  • rand (37-41)
  • g (62-64)
  • g (244-314)
  • g (67-69)
pkgs/generator/crypto.go (3)
  • ValidateAddress (95-104)
  • ValidatePubKey (107-117)
  • rand (12-14)
experiments/experiment7/main.go (2)
  • testKeyGeneration (24-39)
  • main (41-43)
indexer/decoder/decoder.go (2)
indexer/decoder/types.go (1)
  • BasicTxData (7-13)
experiments/experiment6/main.go (1)
  • printAllData (27-44)
pkgs/database/types_api.go (1)
experiments/experiment6/main.go (1)
  • printAllData (27-44)
api/huma-types/transaction.go (2)
api/huma-types/address.go (1)
  • Body (15-17)
api/handlers/transactions_test.go (1)
  • TestTransactionsHandler_GetTransactionBasic_Success (56-81)
indexer/query/types.go (5)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
indexer/rpc_client/errors.go (1)
  • RpcCommitError (36-40)
indexer/rpc_client/blocks.go (2)
  • Type (49-58)
  • br (136-141)
indexer/rpc_client/rmc_test.go (1)
  • m (22-26)
indexer/rpc_client/gen_data.go (1)
  • br (77-80)
integration/synthetic/init.go (2)
indexer/orchestrator/operator_test.go (1)
  • MockDatabaseHeight (97-100)
api/handlers/blocks_test.go (2)
  • TestBlocksHandler_GetLatestBlockHeight_Fail (107-118)
  • TestBlocksHandler_GetLatestBlockHeight_Success (96-105)
indexer/query/operator.go (4)
indexer/orchestrator/types.go (1)
  • QueryOperator (21-26)
indexer/query/types.go (1)
  • QueryOperator (16-22)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
indexer/retry/worker.go (1)
  • RetryWithContext (97-120)
indexer/rpc_client/client.go (3)
indexer/rpc_client/types.go (1)
  • RpcGnoland (10-13)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
indexer/rpc_client/errors.go (1)
  • RpcCommitError (36-40)
indexer/rpc_client/commit.go (2)
indexer/rpc_client/types.go (1)
  • JsonRpcError (16-20)
indexer/rpc_client/blocks.go (3)
  • BlockHeader (22-39)
  • BlockID (10-13)
  • Precommit (49-58)
pkgs/sql_data_types/table.go (3)
pkgs/database/types_api.go (1)
  • Amount (27-30)
pkgs/sql_data_types/types.go (1)
  • Amount (8-11)
pkgs/generator/general.go (1)
  • Amount (84-87)
indexer/orchestrator/types.go (3)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
indexer/rpc_client/blocks.go (1)
  • BlockResponse (86-91)
indexer/rpc_client/transactions.go (1)
  • TxResponse (43-48)
indexer/query/operator_test.go (5)
indexer/rpc_client/blocks.go (1)
  • BlockResponse (86-91)
indexer/rpc_client/errors.go (3)
  • RpcHeightError (6-10)
  • RpcStringError (21-25)
  • RpcCommitError (36-40)
indexer/rpc_client/transactions.go (1)
  • TxResponse (43-48)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
indexer/query/operator.go (1)
  • NewQueryOperator (20-46)
indexer/data_processor/operator.go (3)
pkgs/database/types_api.go (3)
  • MsgCall (42-53)
  • MsgRun (68-79)
  • AddressTx (98-102)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
indexer/decoder/product.go (1)
  • DbMessageGroups (151-156)
indexer/data_processor/types.go (2)
pkgs/sql_data_types/table.go (8)
  • Blocks (101-108)
  • ValidatorBlockSigning (139-150)
  • TransactionGeneral (229-243)
  • MsgSend (291-302)
  • MsgCall (360-373)
  • MsgAddPackage (427-441)
  • MsgRun (495-509)
  • AddressTx (182-188)
pkgs/database/types_api.go (4)
  • MsgCall (42-53)
  • MsgAddPackage (55-66)
  • MsgRun (68-79)
  • AddressTx (98-102)
pkgs/database/queries_indexer.go (1)
pkgs/database/types.go (1)
  • TimescaleDb (10-12)
api/handlers/interface.go (1)
pkgs/database/types_api.go (8)
  • AddressTx (98-102)
  • BlockData (6-13)
  • BlockSigners (92-96)
  • Transaction (81-90)
  • BankSend (32-40)
  • MsgCall (42-53)
  • MsgAddPackage (55-66)
  • MsgRun (68-79)
pkgs/database/insert.go (3)
pkgs/database/types.go (1)
  • TimescaleDb (10-12)
pkgs/sql_data_types/table.go (8)
  • Blocks (101-108)
  • ValidatorBlockSigning (139-150)
  • TransactionGeneral (229-243)
  • AddressTx (182-188)
  • MsgSend (291-302)
  • MsgCall (360-373)
  • MsgAddPackage (427-441)
  • MsgRun (495-509)
pkgs/database/types_api.go (4)
  • AddressTx (98-102)
  • MsgCall (42-53)
  • MsgAddPackage (55-66)
  • MsgRun (68-79)
integration/synthetic/respone_maker.go (4)
pkgs/generator/general.go (1)
  • DataGenerator (37-41)
indexer/rpc_client/blocks.go (3)
  • Precommit (49-58)
  • BlockID (10-13)
  • BlockHeader (22-39)
indexer/rpc_client/transactions.go (1)
  • TxResponse (43-48)
indexer/rpc_client/commit.go (4)
  • CommitResponse (10-15)
  • CommitResult (18-20)
  • SignedHeader (23-26)
  • Commit (29-32)
indexer/orchestrator/operator_test.go (2)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
integration/synthetic/init.go (1)
  • MockDatabaseHeight (81-83)
api/handlers/database_test.go (1)
pkgs/database/types_api.go (8)
  • BlockData (6-13)
  • BlockSigners (92-96)
  • Transaction (81-90)
  • AddressTx (98-102)
  • BankSend (32-40)
  • MsgCall (42-53)
  • MsgAddPackage (55-66)
  • MsgRun (68-79)
pkgs/database/queries_api.go (3)
pkgs/database/types.go (1)
  • TimescaleDb (10-12)
pkgs/database/types_api.go (8)
  • BlockData (6-13)
  • BlockSigners (92-96)
  • BankSend (32-40)
  • Amount (27-30)
  • MsgCall (42-53)
  • MsgAddPackage (55-66)
  • MsgRun (68-79)
  • Transaction (81-90)
pkgs/sql_data_types/table.go (3)
  • MsgCall (360-373)
  • MsgAddPackage (427-441)
  • MsgRun (495-509)
indexer/orchestrator/operator.go (3)
indexer/orchestrator/types.go (1)
  • Orchestrator (50-60)
indexer/rpc_client/blocks.go (1)
  • BlockResponse (86-91)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
integration/synthetic/query_operator.go (2)
indexer/rpc_client/commit.go (1)
  • CommitResponse (10-15)
integration/synthetic/respone_maker.go (3)
  • ResponseMaker (11-13)
  • NewResponseMaker (15-19)
  • GenCommitInput (169-175)
🪛 checkmake (0.2.2)
Makefile

[warning] 1-1: Missing required phony target "all"

(minphony)

🪛 LanguageTool
CHANGELOG.md

[style] ~14-~14: Consider removing “of” to be more concise
Context: ...from the last commit, which is actually all of the block signers from the previous block. ...

(ALL_OF_THE)


[grammar] ~14-~14: Ensure spelling is correct
Context: ... signers from the previous block. So it woult insert it like it was meant for that bl...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~15-~15: Ensure spelling is correct
Context: ...hold this data properly and would cause and error because in the postgres primary k...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~15-~15: Ensure spelling is correct
Context: ...nd would cause and error because in the postgres primary key was attached making each me...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~21-~21: Ensure spelling is correct
Context: ...his is that indexer at that moment will fecth 2X times more request to the RPC, and i...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: integration-test-small
  • GitHub Check: Analyze (go)

Comment thread api/main.go
Comment thread CHANGELOG.md Outdated
Comment thread indexer/data_processor/operator.go
Comment thread Makefile Outdated
Comment thread pkgs/generator/generator_test.go
Comment thread pkgs/generator/generator_test.go
…n binary, removed expermintal-install option from the make file

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
indexer/data_processor/operator.go (2)

247-271: Potential data corruption: slice truncation doesn't account for sparse indices.

When goroutines return early (lines 228, 234, 238, 244), they leave zero-values at their index positions. The slice is written at specific indices (transactionsData[idx]), but truncation at line 271 assumes validCount valid entries are contiguous at the start.

Example: If transactions 0, 2, 4 succeed and 1, 3 fail, validCount=3 but valid data is at indices 0, 2, 4. Truncating to [:3] includes the zero-value at index 1.

Consider using append with mutex instead:

-	transactionsData := make([]sqlDataTypes.TransactionGeneral, transactionAmount)
+	transactionsData := make([]sqlDataTypes.TransactionGeneral, 0, transactionAmount)
 	var mu sync.Mutex
-	var validCount int
 	wg := sync.WaitGroup{}
 ...
-			transactionsData[idx] = sqlDataTypes.TransactionGeneral{...}
-			validCount++
+			mu.Lock()
+			transactionsData = append(transactionsData, sqlDataTypes.TransactionGeneral{...})
 			mu.Unlock()
 ...
-	transactionsData = transactionsData[:validCount]

338-342: Message-to-transaction order mismatch due to unordered channel collection.

Goroutines send decoded messages to addressCollectionChan in non-deterministic order. When collected at lines 340-342, allDecodedMsgs won't maintain the same order as transactions. Later at line 409, allDecodedMsgs[idx] is paired with transactions[idx], causing mismatched pairings.

Consider using index-based assignment like ProcessBlocks:

-	addressCollectionChan := make(chan []*decoder.DecodedMsg, transactionAmount)
+	allDecodedMsgs := make([]*decoder.DecodedMsg, transactionAmount)
 	wg1 := sync.WaitGroup{}
 	wg1.Add(transactionAmount)

-	for _, transaction := range transactions {
-		go func(transaction TrasnactionsData) {
+	for idx, transaction := range transactions {
+		go func(idx int, transaction TrasnactionsData) {
 			defer wg1.Done()
 			decodedMsg := decoder.NewDecodedMsg(transaction.Response.Result.Tx)
 			if decodedMsg == nil {
-				addressCollectionChan <- []*decoder.DecodedMsg{nil}
+				// nil already at allDecodedMsgs[idx]
 				return
 			}
 			// ... address collection ...
-			addressCollectionChan <- []*decoder.DecodedMsg{decodedMsg}
-		}(transaction)
+			allDecodedMsgs[idx] = decodedMsg
+		}(idx, transaction)
 	}
-	// Remove channel closing goroutine and channel collection loop
+	wg1.Wait()
♻️ Duplicate comments (1)
CHANGELOG.md (1)

14-15: Fix remaining grammar and capitalization errors in release notes.

Several issues from previous review feedback remain unfixed:

  • Line 14: "all of the block signers" has redundant "of" (should be "all the block signers")
  • Line 15: "postgres" should be capitalized as "Postgres" (product name)
  • Line 21: "request" is singular but should be plural "requests"; also "this requests" should be "these requests"
- The indexer would go into the blocks data and store the signers from the last commit, which is actually all of the block signers from the previous block. So it would insert it like it was meant for that block height. From now on the indexer will fetch data from the /commit method and insert it properly. [de40740](...)
+ The indexer would go into the blocks data and store the signers from the last commit, which is actually all the block signers from the previous block. So it would insert it like it was meant for that block height. From now on the indexer will fetch data from the /commit method and insert it properly. [de40740](...)

- Gnoland can indeed execute multiple message types in the same transaction. The indexer wasn't able to hold this data properly and would cause an error because in the postgres primary key was attached making each message type unique. Now each message type has a message_counter which is a smallint(int16) which is used as a index for that transaction. [d647901](...)
+ Gnoland can indeed execute multiple message types in the same transaction. The indexer wasn't able to hold this data properly and would cause an error because in the Postgres primary key was attached making each message type unique. Now each message type has a message_counter which is a smallint(int16) which is used as a index for that transaction. [d647901](...)

- The Orchestrator needs to make a fetch to the commit RPC method. This operation is done side by side with fetching the blocks method. A side effect of this is that indexer at that moment will fetch 2 times more request to the RPC, and if the Gnoland node has a limit on the amount of RPC clients that can use it, it might cause the indexer to slow down and throw errors on this requests. In future releases this should be improved. [d647901](...)
+ The Orchestrator needs to make a fetch to the commit RPC method. This operation is done side by side with fetching the blocks method. A side effect of this is that indexer at that moment will fetch 2 times more requests to the RPC, and if the Gnoland node has a limit on the amount of RPC clients that can use it, it might cause the indexer to slow down and throw errors on these requests. In future releases this should be improved. [d647901](...)

Also applies to: 21-21

🧹 Nitpick comments (5)
indexer/data_processor/operator.go (4)

62-98: LGTM! Consider batching lock operations for performance.

The mutex-protected map pattern is correct. For slight performance improvement, you could collect addresses locally within each goroutine and lock once to merge them:

// Inside goroutine - collect locally first
localAddrs := make([]string, 0, len(precommits)+1)
for _, precommit := range precommits {
    if precommit != nil {
        localAddrs = append(localAddrs, precommit.ValidatorAddress)
    }
}
localAddrs = append(localAddrs, proposer)

// Single lock to merge
mu.Lock()
for _, addr := range localAddrs {
    addressesMap[addr] = struct{}{}
}
mu.Unlock()

This reduces lock contention in high-volume scenarios.


162-172: Mutex is unnecessary for index-based slice writes.

Since each goroutine writes to a distinct index blocksData[idx] in a preallocated slice, concurrent writes are safe without synchronization. The slice header isn't modified (no append), only element assignment.

-			// Use mutex only when writing to shared slice
-			mu.Lock()
 			blocksData[idx] = sqlDataTypes.Blocks{
 				Hash:      hash,
 				Height:    height,
 				Timestamp: block.Result.Block.Header.Time,
 				ChainID:   block.Result.Block.Header.ChainID,
 				Txs:       txs,
 				ChainName: d.chainName,
 			}
-			mu.Unlock()

You can also remove the var mu sync.Mutex declaration on line 122.


580-584: LGTM! Minor: prefer defer cancel() for consistency.

The commit-based processing logic is correct. For consistency with other functions (e.g., line 181), consider using defer cancel():

 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
 	err := d.dbPool.InsertValidatorBlockSignings(ctx, validatorData)
-	cancel()

603-604: Consider preallocating the addresses slice.

The slice grows via append under mutex contention. Since you know txAmount, you can estimate and preallocate:

-	var addresses []sqlDataTypes.AddressTx
+	// Estimate ~2 addresses per message on average
+	addresses := make([]sqlDataTypes.AddressTx, 0, txAmount*2)

This reduces allocations and potential slice copying during concurrent appends.

Makefile (1)

1-6: Consider adding an "all" phony target for convenience.

While not required, adding an all target is a common Makefile convention that provides a default build action when running make without arguments. This typically builds all main artifacts.

You could add this after line 6:

all: build build-api

And update the .PHONY declaration:

-.PHONY: build install clean build-experimental install-experimental build-api test-race integration-test test
+.PHONY: all build install clean build-experimental build-api test-race integration-test test
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa49536 and 8330250.

📒 Files selected for processing (5)
  • CHANGELOG.md (1 hunks)
  • Makefile (2 hunks)
  • api/main.go (4 hunks)
  • indexer/data_processor/operator.go (24 hunks)
  • pkgs/generator/generator_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkgs/generator/generator_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
indexer/data_processor/operator.go (2)
indexer/decoder/types.go (1)
  • DecodedMsg (34-37)
indexer/decoder/product.go (1)
  • DbMessageGroups (151-156)
🪛 checkmake (0.2.2)
Makefile

[warning] 1-1: Missing required phony target "all"

(minphony)

🪛 LanguageTool
CHANGELOG.md

[style] ~14-~14: Consider removing “of” to be more concise
Context: ...from the last commit, which is actually all of the block signers from the previous block. ...

(ALL_OF_THE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: integration-test-small
  • GitHub Check: Analyze (go)
🔇 Additional comments (9)
indexer/data_processor/operator.go (1)

451-508: Timeout formula fixed - consistent pattern across all message types.

The timeout calculation 10*time.Second + (time.Duration(count) * time.Second / 5) correctly adds 0.2 seconds per message, addressing the issue from the previous review. The pattern is now consistent with other insert operations.

api/main.go (4)

4-4: LGTM! Standard embed pattern for serving static assets.

The embed directive and imports are correctly structured for serving the favicon as an embedded asset.

Also applies to: 8-8, 22-23


111-128: LGTM! Comprehensive OpenAPI metadata setup.

The dynamic OpenAPI info construction provides complete API documentation metadata including contact details, license, and external documentation links. Version normalization via TrimPrefix ensures consistent formatting.


151-160: LGTM! Previous critical issue has been addressed.

The favicon handler now correctly handles write errors gracefully using log.Printf and http.Error instead of log.Fatalf, preventing the server from crashing on transient failures.


163-215: LGTM! Consistent per-route OpenAPI metadata.

The operation callbacks provide clear summaries and descriptions for each endpoint, enhancing the API documentation quality in a consistent manner across all routes.

Makefile (4)

1-1: LGTM! .PHONY list correctly updated.

The phony targets list now includes the new test targets and correctly removes install-experimental which was deleted from the Makefile.


11-12: LGTM! Enhanced version derivation for development builds.

The addition of GIT_BRANCH and updated VERSION logic provides better version identification for untagged builds (e.g., "dev-a1b2c3d"), improving traceability during development.


37-44: LGTM! Well-structured test targets.

The test targets follow Go best practices:

  • test runs all tests with verbose output
  • test-race enables the race detector for concurrency issues
  • integration-test properly isolates integration tests with build tags and includes a generous 20-minute timeout for long-running tests

16-16: File rename successfully completed — no issues found.

The verification confirms that:

  • indexer/indexer.go exists with package main and func main() at line 9
  • indexer/main.go has been properly removed
  • The build, install, and experimental build targets correctly reference the new entry point

The structural change in the Makefile is valid.

// Phase 1: Concurrent address collection using sync.Map
var addressesMap sync.Map
addressCollectionChan := make(chan []*decoder.DecodedMsg, len(transactions))
// Phase 1: Concurrent address collection using map[strinct]struct{}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo in comment.

map[strinct]struct{} should be map[string]struct{}.

🤖 Prompt for AI Agents
In indexer/data_processor/operator.go around line 302, the inline comment
contains a typo "map[strinct]struct{}" — update the comment to
"map[string]struct{}" so it accurately documents the data structure being used;
no code changes required beyond correcting the comment text.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8330250 and 89e1d72.

📒 Files selected for processing (1)
  • .github/workflows/integration-tests.yml (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: integration-test-small
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
.github/workflows/integration-tests.yml (1)

2-4: LGTM: Permissions block follows least-privilege principle.

Adding explicit read permissions for contents and packages is a security best practice for CI workflows.

- name: Initialize database schema
run: |
go run indexer/main.go setup create-db --db-user postgres --db-name postgres
go run indexer/indexer.go setup create-db --db-user postgres --db-name postgres

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Database schema mismatch in small test setup.

The setup command creates the schema in the postgres database (--db-name postgres), but the test config connects to the gnoland database (line 75). The schema won't exist in the target database, causing test failures.

The small test's container (line 33) initializes with POSTGRES_DB: postgres, but the test config should target postgres database, not gnoland. Alternatively, pass --db-name gnoland to the setup command.

Proposed fix (option 1: align setup with test config):

-          go run indexer/indexer.go setup create-db --db-user postgres --db-name postgres
+          go run indexer/indexer.go setup create-db --db-user postgres --db-name gnoland

Or (option 2: align test config with setup):

-          dbname: gnoland
+          dbname: postgres

Which approach is correct depends on your data model. Please verify.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
go run indexer/indexer.go setup create-db --db-user postgres --db-name postgres
go run indexer/indexer.go setup create-db --db-user postgres --db-name gnoland
🤖 Prompt for AI Agents
In .github/workflows/integration-tests.yml around line 60, the setup command
creates the schema in the postgres database (--db-name postgres) while the test
config targets gnoland (line 75), causing a schema mismatch; fix by either (A)
changing the setup command to use --db-name gnoland so the schema is created in
the database the tests connect to, or (B) change the test configuration (and any
DB env like POSTGRES_DB) to use postgres so it matches the setup step—pick the
correct option for your data model and update the workflow lines accordingly so
setup and test DB names are identical.

- name: Initialize database schema
run: |
go run indexer/main.go setup create-db
go run indexer/indexer.go setup create-db --db-user postgres --db-name postgres

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Database schema mismatch in medium test setup.

Same issue as the small test: the setup command creates the schema in the postgres database (--db-name postgres), but the test config connects to gnoland (line 149). The schema won't exist in the target database.

Unlike the small test, the container (line 107) initializes with POSTGRES_DB: gnoland, suggesting the intended target is gnoland. Pass --db-name gnoland to the setup command:

-          go run indexer/indexer.go setup create-db --db-user postgres --db-name postgres
+          go run indexer/indexer.go setup create-db --db-user postgres --db-name gnoland
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
go run indexer/indexer.go setup create-db --db-user postgres --db-name postgres
go run indexer/indexer.go setup create-db --db-user postgres --db-name gnoland
🤖 Prompt for AI Agents
.github/workflows/integration-tests.yml around line 134: the setup step runs the
indexer with --db-name postgres while the Postgres container is initialized with
POSTGRES_DB: gnoland and the test config connects to gnoland, causing a schema
mismatch. Update the setup command to pass --db-name gnoland (so the schema is
created in the same database the tests connect to) and verify any other DB-name
references in this workflow match gnoland.

- name: Initialize database schema
run: |
go run indexer/main.go setup create-db
go run indexer/indexer.go setup create-db

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Script executed:

#!/bin/bash
# Examine the workflow file around the relevant lines
cat -n .github/workflows/integration-tests.yml | sed -n '55,65p; 130,140p; 205,215p'

Repository: Cogwheel-Validator/spectra-gnoland-indexer


<shell_result>
Repository: Cogwheel-Validator/spectra-gnoland-indexer
Exit code: 0

stdout:

    55	        env:
    56	          PGPASSWORD: test_password_12345678
    57	
    58	      - name: Initialize database schema
    59	        run: |
    60	          go run indexer/indexer.go setup create-db --db-user postgres --db-name postgres
    61	        env:
    62	          DB_HOST: localhost
    63	          DB_PORT: 5432
    64	          DB_USER: gnoland
    65	          DB_PASSWORD: test_password_12345678
   130	          PGPASSWORD: test_password_12345678
   131	
   132	      - name: Initialize database schema
   133	        run: |
   134	          go run indexer/indexer.go setup create-db --db-user postgres --db-name postgres
   135	        env:
   136	          DB_HOST: localhost
   137	          DB_PORT: 5432
   138	          DB_USER: postgres
   139	          DB_PASSWORD: test_password_12345678
   140	          DB_NAME: gnoland
   205	
   206	      - name: Initialize database schema
   207	        run: |
   208	          go run indexer/indexer.go setup create-db
   209	        env:
   210	          DB_HOST: localhost
   211	          DB_PORT: 5432
   212	          DB_USER: postgres
   213	          DB_PASSWORD: test_password_12345678
   214	          DB_NAME: gnoland
   215	

</shell_result>


Script executed:

#!/bin/bash
# Find indexer.go and examine its setup and create-db command handling
find . -name "indexer.go" -type f | head -5

Repository: Cogwheel-Validator/spectra-gnoland-indexer


<shell_result>
Repository: Cogwheel-Validator/spectra-gnoland-indexer
Exit code: 0

stdout:

./indexer/indexer.go

</shell_result>


Script executed:

#!/bin/bash
# Check file size then examine setup create-db command handling
wc -l ./indexer/indexer.go

Repository: Cogwheel-Validator/spectra-gnoland-indexer


<shell_result>
Repository: Cogwheel-Validator/spectra-gnoland-indexer
Exit code: 0

stdout:

13 ./indexer/indexer.go

</shell_result>


Script executed:

#!/bin/bash
# Read the full indexer.go file and search for setup command implementation
cat -n ./indexer/indexer.go && find ./indexer -name "*.go" -type f | grep -E "(setup|cmd)" | head -10

Repository: Cogwheel-Validator/spectra-gnoland-indexer


<shell_result>
Repository: Cogwheel-Validator/spectra-gnoland-indexer
Exit code: 0

stdout:

     1	package main
     2	
     3	import (
     4		"log"
     5	
     6		"github.com/Cogwheel-Validator/spectra-gnoland-indexer/indexer/cmd"
     7	)
     8	
     9	func main() {
    10		if err := cmd.RootCmd.Execute(); err != nil {
    11			log.Fatalf("failed to execute command: %v", err)
    12		}
    13	}
./indexer/cmd/func.go
./indexer/cmd/historic.go
./indexer/cmd/live.go
./indexer/cmd/root.go
./indexer/cmd/run.go
./indexer/cmd/setup.go

</shell_result>


Script executed:

#!/bin/bash
# Check setup.go size and examine it
wc -l ./indexer/cmd/setup.go
cat -n ./indexer/cmd/setup.go

Repository: Cogwheel-Validator/spectra-gnoland-indexer


<shell_result>
Repository: Cogwheel-Validator/spectra-gnoland-indexer
Exit code: 0

stdout:

272 ./indexer/cmd/setup.go
     1	package cmd
     2	
     3	import (
     4		"context"
     5		"log"
     6		"time"
     7	
     8		dbinit "github.com/Cogwheel-Validator/spectra-gnoland-indexer/indexer/db_init"
     9		"github.com/Cogwheel-Validator/spectra-gnoland-indexer/pkgs/database"
    10		"github.com/Cogwheel-Validator/spectra-gnoland-indexer/pkgs/sql_data_types"
    11		"github.com/spf13/cobra"
    12	)
    13	
    14	var allowedSslModes = []string{"disable", "require", "verify-ca", "verify-full", "allow", "prefer"}
    15	
    16	// dbParams holds common database connection parameters
    17	type dbParams struct {
    18		host     string
    19		port     int
    20		user     string
    21		name     string
    22		password string
    23		sslMode  string
    24	}
    25	
    26	var setupCmd = &cobra.Command{
    27		Use:   "setup",
    28		Short: "Database setup tools",
    29		Long:  `A collection of tools to set up and manage the database for the gnoland indexer.`,
    30	}
    31	
    32	func init() {
    33		// Add subcommands
    34		setupCmd.AddCommand(createDbCmd)
    35		setupCmd.AddCommand(createUserCmd)
    36		setupCmd.AddCommand(createConfigCmd)
    37	
    38		// Common flags for both database setup commands
    39		for _, cmd := range []*cobra.Command{createDbCmd, createUserCmd} {
    40			cmd.Flags().StringP("db-host", "b", "", "The database host, default is localhost")
    41			cmd.Flags().IntP("db-port", "p", 0, "The database port, default is 5432")
    42			cmd.Flags().StringP("db-user", "u", "", "The database user, default is postgres")
    43			cmd.Flags().StringP("db-name", "d", "", "The database name, default is postgres")
    44			cmd.Flags().StringP("ssl-mode", "s", "", "The SSL mode for the database connection, default is disable")
    45		}
    46	
    47		// create-user specific flags
    48		createUserCmd.Flags().StringP("privilege", "r", "", "The privilege level for the user (reader or writer)")
    49		createUserCmd.Flags().String("user", "", "The user name for the user to create")
    50	
    51		// create-db specific flags
    52		createDbCmd.Flags().String("new-db-name", "", "The database name to create, default is gnoland")
    53		createDbCmd.Flags().String("chain-name", "", "The chain name for the database type enum, default is gnoland")
    54	
    55		// create-config specific flags
    56		createConfigCmd.Flags().StringP("config", "c", "config.yaml", "The config file name, default is config.yaml")
    57		createConfigCmd.Flags().BoolP("overwrite", "o", false, "Overwrite the existing config file, default is false")
    58	}
    59	
    60	var createDbCmd = &cobra.Command{
    61		Use:   "create-db",
    62		Short: "Create a new database named gnoland",
    63		Long: `Create a new database named gnoland for the indexer. It goes\n
    64		throught a lot of steps to create the database and insert the tables and data.`,
    65		RunE: func(cmd *cobra.Command, args []string) error {
    66			log.Printf("Initiating the cmd to set up the database for the indexer...")
    67	
    68			// Parse and validate common database flags
    69			params, err := parseCommonFlags(cmd, "postgres")
    70			if err != nil {
    71				log.Fatalf("failed to parse flags: %v", err)
    72			}
    73	
    74			// get the new database name from the flags
    75			newDbName, _ := cmd.Flags().GetString("new-db-name")
    76			if newDbName == "" {
    77				newDbName = "gnoland"
    78			}
    79	
    80			// get the chain name from the flags
    81			chainName, _ := cmd.Flags().GetString("chain-name")
    82			if chainName == "" {
    83				chainName = "gnoland"
    84			}
    85	
    86			// Prompt for password
    87			params.password, err = promptPassword()
    88			if err != nil {
    89				log.Fatalf("failed to read password: %v", err)
    90			}
    91	
    92			// Create database config
    93			dbConfig := params.createDatabaseConfig()
    94	
    95			// create a new database connection
    96			db := database.NewTimescaleDbSetup(dbConfig)
    97	
    98			// create a new database named "gnoland"
    99			// but check if the current database is "gnoland"
   100			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   101			defer cancel()
   102			currentDb, err := db.CheckCurrentDatabaseName(ctx)
   103			if err != nil {
   104				log.Fatalf("failed to check current database name: %v", err)
   105			}
   106			log.Printf("Logged in into the database %s", currentDb)
   107	
   108			// if the current database is not "gnoland", create a new database named "gnoland"
   109			// and insert all of the tables and data from the "gnoland" database
   110			if currentDb != newDbName {
   111				// create a new database named "gnoland"
   112				log.Printf("Creating a new database named %s", newDbName)
   113				err = database.CreateDatabase(db, newDbName)
   114				if err != nil {
   115					log.Fatalf("failed to create database: %v", err)
   116				}
   117				// switch to the new database
   118				log.Printf("Switching to the new database %s", newDbName)
   119				// only for now later add dbName value, it is only for the testing now
   120				err = database.SwitchDatabase(db, dbConfig, newDbName)
   121				if err != nil {
   122					log.Fatalf("failed to switch database: %v", err)
   123				}
   124				// insert all of the tables and data from the new database
   125				// First create special types (custom postgres types that tables depend on)
   126				// and type enums
   127				specialTypes := []sql_data_types.DBSpecialType{
   128					sql_data_types.Amount{},
   129					sql_data_types.Attribute{}, // this needs to be inserted prior to event type
   130					sql_data_types.Event{},
   131				}
   132				typeEnums := []string{
   133					chainName,
   134				}
   135	
   136				// Initialize database initializer
   137				dbInit := dbinit.NewDBInitializer(db.GetPool())
   138	
   139				// Create special types first (they need to exist before tables that use them)
   140				log.Printf("Inserting all of the special types into the %s database", chainName)
   141				for _, specialType := range specialTypes {
   142					err = dbInit.CreateSpecialTypeFromStruct(specialType, specialType.TypeName())
   143					if err != nil {
   144						log.Fatalf("failed to create special type %s: %v", specialType.TypeName(), err)
   145					}
   146				}
   147	
   148				// Create type enums
   149				log.Printf("Inserting all of the type enums into the %s database", chainName)
   150				err = dbInit.CreateChainTypeEnum(typeEnums)
   151				if err != nil {
   152					log.Fatalf("failed to create type enum %s: %v", typeEnums, err)
   153				}
   154	
   155				// Create regular tables (non-time-series tables)
   156				log.Printf("Inserting all of the regular tables into the %s database", chainName)
   157				regularTables := []sql_data_types.DBTable{
   158					sql_data_types.GnoAddress{},
   159					sql_data_types.GnoValidatorAddress{},
   160				}
   161	
   162				for _, dataType := range regularTables {
   163					err = dbInit.CreateTableFromStruct(dataType, dataType.TableName())
   164					if err != nil {
   165						log.Fatalf("failed to create table %s: %v", dataType.TableName(), err)
   166					}
   167				}
   168	
   169				// Create hypertables (time-series tables with timestamp columns)
   170				log.Printf("Inserting all of the hypertables into the %s database", chainName)
   171				hypertables := []struct {
   172					table           sql_data_types.DBTable
   173					partitionColumn string
   174					chunkInterval   string
   175				}{
   176					{sql_data_types.Blocks{}, "timestamp", "1 week"},
   177					{sql_data_types.ValidatorBlockSigning{}, "timestamp", "1 week"},
   178					{sql_data_types.AddressTx{}, "timestamp", "1 week"},
   179					{sql_data_types.TransactionGeneral{}, "timestamp", "1 week"},
   180					{sql_data_types.MsgSend{}, "timestamp", "1 week"},
   181					{sql_data_types.MsgCall{}, "timestamp", "1 week"},
   182					{sql_data_types.MsgAddPackage{}, "timestamp", "1 week"},
   183					{sql_data_types.MsgRun{}, "timestamp", "1 week"},
   184				}
   185	
   186				for _, ht := range hypertables {
   187					err = dbInit.CreateHypertableFromStruct(ht.table, ht.table.TableName(), ht.partitionColumn, ht.chunkInterval)
   188					if err != nil {
   189						log.Fatalf("failed to create hypertable %s: %v", ht.table.TableName(), err)
   190					}
   191				}
   192				log.Printf("Successfully created all of the hypertables into the %s database", chainName)
   193			} else {
   194				log.Printf("The current database is %s, and it already exists", currentDb)
   195				// TODO else if the current database is "gnoland" then we need to check if the tables exist
   196				// and if they don't exist then we need to create them
   197				// also any kind of future updates to the database should be done here
   198			}
   199			return nil
   200		},
   201	}
   202	
   203	var createUserCmd = &cobra.Command{
   204		Use:   "create-user",
   205		Short: "Create a new user for the database",
   206		Long:  `Create a new user for the database. It will ask for the password and create the user.`,
   207		RunE: func(cmd *cobra.Command, args []string) error {
   208			// Parse and validate common database flags
   209			params, err := parseCommonFlags(cmd, "gnoland")
   210			if err != nil {
   211				log.Fatalf("failed to parse flags: %v", err)
   212			}
   213	
   214			// Get privilege flag
   215			privilege, _ := cmd.Flags().GetString("privilege")
   216			if privilege == "" {
   217				log.Fatalf("privilege is required")
   218			} else if privilege != "reader" && privilege != "writer" {
   219				log.Fatalf("invalid privilege: %s", privilege)
   220			}
   221	
   222			// get the user name from the flags
   223			userName, _ := cmd.Flags().GetString("user")
   224			if userName == "" {
   225				log.Fatalf("user name is required")
   226			}
   227	
   228			// Prompt for password
   229			params.password, err = promptPassword()
   230			if err != nil {
   231				log.Fatalf("failed to read password: %v", err)
   232			}
   233	
   234			// Create database config and connection
   235			dbConfig := params.createDatabaseConfig()
   236			db := database.NewTimescaleDbSetup(dbConfig)
   237			dbInit := dbinit.NewDBInitializer(db.GetPool())
   238	
   239			// Create a new user
   240			err = dbInit.CreateUser(userName)
   241			if err != nil {
   242				log.Fatalf("failed to create user: %v", err)
   243			}
   244	
   245			// Appoint privileges to the user
   246			err = dbInit.AppointPrivileges(params.user, privilege, []string{})
   247			if err != nil {
   248				log.Fatalf("failed to appoint privileges to user: %v", err)
   249			}
   250	
   251			log.Printf("Successfully created user %s with privilege %s", params.user, privilege)
   252			return nil
   253		},
   254	}
   255	
   256	var createConfigCmd = &cobra.Command{
   257		Use:   "create-config",
   258		Short: "Generate a config with default values.",
   259		Long: `Generate a config with default values. It will make a config file with default values. 
   260		You can add --overwrite to overwrite the existing config file. And you can use --config to specifly the path`,
   261		RunE: func(cmd *cobra.Command, args []string) error {
   262			// get the config file name from the flags
   263			configFileName, _ := cmd.Flags().GetString("config")
   264			if configFileName == "" {
   265				configFileName = "config.yaml"
   266			}
   267			overwrite, _ := cmd.Flags().GetBool("overwrite")
   268			createConfig(overwrite, configFileName)
   269			log.Printf("Successfully created config file %s", configFileName)
   270			return nil
   271		},
   272	}

</shell_result>


Script executed:

#!/bin/bash
# Find parseCommonFlags function definition
grep -n "func parseCommonFlags" ./indexer/cmd/*.go

Repository: Cogwheel-Validator/spectra-gnoland-indexer


<shell_result>
Repository: Cogwheel-Validator/spectra-gnoland-indexer
Exit code: 0

stdout:

./indexer/cmd/func.go:20:func parseCommonFlags(cmd *cobra.Command, defaultDbName string) (*dbParams, error) {

</shell_result>


Script executed:

#!/bin/bash
# Read func.go to understand parseCommonFlags and env var handling
cat -n ./indexer/cmd/func.go

Repository: Cogwheel-Validator/spectra-gnoland-indexer


<shell_result>
Repository: Cogwheel-Validator/spectra-gnoland-indexer
Exit code: 0

stdout:

     1	package cmd
     2	
     3	import (
     4		"fmt"
     5		"log"
     6		"os"
     7		"path/filepath"
     8		"slices"
     9		"syscall"
    10		"time"
    11	
    12		"github.com/Cogwheel-Validator/spectra-gnoland-indexer/indexer/config"
    13		"github.com/Cogwheel-Validator/spectra-gnoland-indexer/pkgs/database"
    14		"github.com/spf13/cobra"
    15		"go.yaml.in/yaml/v4"
    16		"golang.org/x/term"
    17	)
    18	
    19	// parseCommonFlags extracts and validates common database flags
    20	func parseCommonFlags(cmd *cobra.Command, defaultDbName string) (*dbParams, error) {
    21		params := &dbParams{}
    22	
    23		params.host, _ = cmd.Flags().GetString("db-host")
    24		params.port, _ = cmd.Flags().GetInt("db-port")
    25		params.user, _ = cmd.Flags().GetString("db-user")
    26		params.sslMode, _ = cmd.Flags().GetString("ssl-mode")
    27		params.name, _ = cmd.Flags().GetString("db-name")
    28	
    29		// Apply environment variable fallbacks (for CI/CD)
    30		if params.host == "" {
    31			if envHost := os.Getenv("DB_HOST"); envHost != "" {
    32				params.host = envHost
    33			}
    34		}
    35		if params.port == 0 {
    36			if envPort := os.Getenv("DB_PORT"); envPort != "" {
    37				if _, err := fmt.Sscanf(envPort, "%d", &params.port); err != nil {
    38					return nil, fmt.Errorf("failed to scan port: %v", err)
    39				}
    40			}
    41		}
    42		if params.user == "" {
    43			if envUser := os.Getenv("DB_USER"); envUser != "" {
    44				params.user = envUser
    45			}
    46		}
    47		if params.name == "" {
    48			if envDbName := os.Getenv("DB_NAME"); envDbName != "" {
    49				params.name = envDbName
    50			}
    51		}
    52	
    53		// Apply defaults if still empty
    54		if params.sslMode == "" {
    55			params.sslMode = "disable"
    56		}
    57		if params.host == "" {
    58			params.host = "localhost"
    59		}
    60		if params.port == 0 {
    61			params.port = 5432
    62		}
    63		if params.user == "" {
    64			params.user = "postgres"
    65		}
    66		if params.name == "" {
    67			params.name = defaultDbName
    68		}
    69	
    70		// Validate
    71		if !slices.Contains(allowedSslModes, params.sslMode) {
    72			return nil, fmt.Errorf("invalid ssl mode: %s", params.sslMode)
    73		}
    74		if params.port < 1 || params.port > 65535 {
    75			return nil, fmt.Errorf("invalid port: %d", params.port)
    76		}
    77	
    78		return params, nil
    79	}
    80	
    81	// promptPassword prompts user for password input or reads from environment
    82	func promptPassword() (string, error) {
    83		// First check if password is provided via environment variable (for CI/CD)
    84		if envPassword := os.Getenv("DB_PASSWORD"); envPassword != "" {
    85			return envPassword, nil
    86		}
    87	
    88		// Interactive mode: prompt user for password
    89		fmt.Print("Enter the database password: ")
    90		bytePassword, err := term.ReadPassword(int(syscall.Stdin))
    91		if err != nil {
    92			return "", fmt.Errorf("failed to read password: %v", err)
    93		}
    94		fmt.Println()
    95		return string(bytePassword), nil
    96	}
    97	
    98	// createDatabaseConfig creates a DatabasePoolConfig from dbParams
    99	func (p *dbParams) createDatabaseConfig() database.DatabasePoolConfig {
   100		return database.DatabasePoolConfig{
   101			Host:                      p.host,
   102			Port:                      p.port,
   103			User:                      p.user,
   104			Dbname:                    p.name,
   105			Password:                  p.password,
   106			Sslmode:                   p.sslMode,
   107			PoolMaxConns:              10,
   108			PoolMinConns:              1,
   109			PoolMaxConnLifetime:       10 * time.Minute,
   110			PoolMaxConnIdleTime:       5 * time.Minute,
   111			PoolHealthCheckPeriod:     1 * time.Minute,
   112			PoolMaxConnLifetimeJitter: 1 * time.Minute,
   113		}
   114	}
   115	
   116	func createConfig(overwrite bool, fileName string) {
   117		if fileName == "" {
   118			fileName = "config.yml"
   119		}
   120		absolutePath, err := filepath.Abs(fileName)
   121		if err != nil {
   122			log.Fatalf("failed to get absolute path: %v", err)
   123		}
   124		fileName = absolutePath
   125		// config file
   126		config := config.Config{
   127			RpcUrl:                    "http://localhost:26657",
   128			PoolMaxConns:              50,
   129			PoolMinConns:              10,
   130			PoolMaxConnLifetime:       5 * time.Minute,
   131			PoolMaxConnIdleTime:       5 * time.Minute,
   132			PoolHealthCheckPeriod:     1 * time.Minute,
   133			PoolMaxConnLifetimeJitter: 1 * time.Minute,
   134			LivePooling:               5 * time.Second,
   135			MaxBlockChunkSize:         50,
   136			MaxTransactionChunkSize:   100,
   137			ChainName:                 "gnoland",
   138			RetryAmount:               &[]int{6}[0],
   139			Pause:                     &[]int{3}[0],
   140			PauseTime:                 &[]time.Duration{15 * time.Second}[0],
   141			ExponentialBackoff:        &[]time.Duration{2 * time.Second}[0],
   142		}
   143		// marshal the config to yaml
   144		yamlFile, err := yaml.Marshal(config)
   145		if err != nil {
   146			log.Fatalf("failed to marshal config: %v", err)
   147		}
   148		// check if the config file exists
   149		if _, err = os.Stat(fileName); err == nil {
   150			if overwrite {
   151				if writeErr := os.WriteFile(fileName, yamlFile, 0644); writeErr != nil {
   152					log.Fatalf("failed to overwrite config file: %v", writeErr)
   153				}
   154			} else {
   155				log.Fatalf("config file already exists, use --overwrite to overwrite it")
   156			}
   157		} else if os.IsNotExist(err) {
   158			if writeErr := os.WriteFile(fileName, yamlFile, 0644); writeErr != nil {
   159				log.Fatalf("failed to create config file: %v", writeErr)
   160			}
   161		} else {
   162			log.Fatalf("failed to stat config file: %v", err)
   163		}
   164	}

</shell_result>


Script executed:

#!/bin/bash
# Verify the default db name logic for create-db
sed -n '65,78p' ./indexer/cmd/setup.go

Repository: Cogwheel-Validator/spectra-gnoland-indexer


<shell_result>
Repository: Cogwheel-Validator/spectra-gnoland-indexer
Exit code: 0

stdout:

	RunE: func(cmd *cobra.Command, args []string) error {
		log.Printf("Initiating the cmd to set up the database for the indexer...")

		// Parse and validate common database flags
		params, err := parseCommonFlags(cmd, "postgres")
		if err != nil {
			log.Fatalf("failed to parse flags: %v", err)
		}

		// get the new database name from the flags
		newDbName, _ := cmd.Flags().GetString("new-db-name")
		if newDbName == "" {
			newDbName = "gnoland"
		}

</shell_result>


Based on my verification, I have confirmed the inconsistency claim. Here's what I found:

Verification Summary:

The parseCommonFlags function implements a clear precedence: command-line flags → environment variables → defaults.

  • Small/Medium tests (lines 60, 134): Explicitly pass --db-user postgres --db-name postgres, which forces connections to the "postgres" database regardless of environment variables.

  • Large test (line 208): Omits these flags and relies on DB_NAME=gnoland (line 214), causing it to connect to the "gnoland" database instead.

This creates functionally different behavior:

  • Small/Medium: Connect to "postgres" DB, then create "gnoland"
  • Large: Connect to "gnoland" DB (from env var), then attempt creation (which is skipped if DB already exists due to line 110 check in setup.go)

The inconsistency is real and impacts initialization behavior across test sizes.


Inconsistency: Large test omits --db-user and --db-name flags, causing different connection database.

The small and medium tests explicitly pass --db-user postgres --db-name postgres (lines 60, 134), connecting to the "postgres" database for initialization. The large test omits these flags (line 208) and instead relies on the DB_NAME=gnoland environment variable (line 214), connecting to "gnoland" instead.

This creates inconsistent initialization behavior: small/medium tests connect to "postgres" to create/initialize "gnoland", while the large test connects directly to "gnoland". If the large test intends to initialize from "postgres" like the other tests, add the explicit flags. If "gnoland" is the intended starting database for large tests, document this intentional difference and verify the schema initialization handles both scenarios correctly.

🤖 Prompt for AI Agents
.github/workflows/integration-tests.yml around line 208: the large test run
omits the explicit db flags and relies on DB_NAME=gnoland which causes it to
connect to a different database than the small/medium tests; to fix, make the
large test consistent with the others by adding the same flags used in
small/medium (e.g. append --db-user postgres --db-name postgres to the go run
indexer/indexer.go setup create-db invocation) or alternatively set
DB_NAME=postgres for that step and ensure the env/flag precedence is documented;
choose the option that matches intended behavior and keep all three test sizes
consistent.

@nman98 nman98 merged commit cfa96cc into main Nov 26, 2025
14 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Mar 5, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Mar 14, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant